#include "Bank.hh"
#include <fstream>
#include <iomanip>
#include <algorithm>
#include <ctime>

Bank::Bank() {}
Bank::Bank(std::vector<Customer*> customers, std::map<std::string, Account*> accounts, std::vector<Transaction> transactions) {
    this->customers = customers;
    this->accounts = accounts;
    this->transactions = transactions;
}

Bank::~Bank() {
    for (auto customer : customers) {
        delete customer;
    }
    for (auto account : accounts) {
        delete account.second;
    }
}
Bank::Bank(const Bank& other) {
    customers = other.customers;
    accounts = other.accounts;
    transactions = other.transactions;
}
Bank& Bank::operator=(const Bank& other) {
    if (this != &other) {
        customers = other.customers;
        accounts = other.accounts;
        transactions = other.transactions;
    }
    return *this;
}

void Bank::addCustomer(const std::string& name) {
    if (findCustomer(name)) {
        std::cout << "Customer " << name << " already exists.\n";
        std::cout << "Press Enter to return to main menu...\n";
        std::cin.ignore();
        std::cin.get();
        return;
    }
    std::string savingsID = Utility::generateAccountID(21);
    std::string checkingID = Utility::generateAccountID(22);
    Customer* newCustomer = new Customer(name, savingsID, checkingID, this);
    customers.push_back(newCustomer);
    accounts[savingsID] = newCustomer->getSavingsAccount();
    accounts[savingsID]->setBank(this); // Set the bank for the savings account
    accounts[checkingID] = newCustomer->getCheckingAccount();
    accounts[checkingID]->setBank(this); // Set the bank for the checking account
    std::cout << "Customer " << name << " registered successfully.\n";
    std::cout << "Two bank accounts have been created:\n";
    std::cout << "Savings Account ID: " << savingsID << "\n";
    std::cout << "Checking Account ID: " << checkingID << "\n";
    std::cout << "Press Enter to return to main menu...\n";
}

Customer* Bank::findCustomer(const std::string& name) {
    for (auto customer:customers) {
        if (customer->getName() == name) {
            return customer;
        }
    }
    return nullptr;
}

CheckingAccount* Bank::findCheckingAccount(const std::string& accountID) {
    auto it = accounts.find(accountID);
    if (it != accounts.end()) {
        return dynamic_cast<CheckingAccount*>(it->second);
    }
    return nullptr;
}

SavingsAccount* Bank::findSavingsAccount(const std::string& accountID) {
    auto it = accounts.find(accountID);
    if (it != accounts.end()) {
        return dynamic_cast<SavingsAccount*>(it->second);
    }
    return nullptr;
}
void Bank::displayAllAccounts() {
    std::cout << "===== All Bank Accounts =====\n";

    std::cout << "\nSAVINGS ACCOUNTS:\n";
    for (const auto& entry : accounts) {
        const Account* account = entry.second;
        if (dynamic_cast<const SavingsAccount*>(account)) {
            std::cout << "ID: " << account->getAccountID() 
                      << " |  Owner: " << account->getOwner() 
                      << " |  Balance: $" << std::fixed << std::setprecision(2) << account->getBalance() << "\n";
        }
    }

    std::cout << "\nCHECKING ACCOUNTS:\n";
    for (const auto& entry : accounts) {
        const Account* account = entry.second;
        if (dynamic_cast<const CheckingAccount*>(account)) {
            std::cout << "ID: " << account->getAccountID() 
                      << " |  Owner: " << account->getOwner() 
                      << " |  Balance: $" << std::fixed << std::setprecision(2) << account->getBalance() << "\n";
        }
    }

    // Calculate and display total statistics
    int totalAccounts = accounts.size();
    int totalCustomers = customers.size();
    double totalBalance = 0.0;
    for (const auto& entry : accounts) {
        totalBalance += entry.second->getBalance();
    }

    std::cout << "\nTotal Accounts: " << totalAccounts << "\n";
    std::cout << "Total Customers: " << totalCustomers << "\n";
    std::cout << "Total Balance: $" << std::fixed << std::setprecision(2) << totalBalance << "\n";

    std::cout << "\nPress Enter to return to main menu...\n";
    std::cin.ignore();
    std::cin.get();
}

void Bank::generateReport() {
    std::string filename = "transactions_" + Utility::getCurrentDateTime() + ".txt";

    std::ofstream reportFile(filename);
    if (!reportFile.is_open()) {
        std::cerr << "Failed to open file: " << filename << std::endl;
        return;
    }

    reportFile << "===== Transaction Reports =====\n";
    reportFile << "Generated on: " << Utility::getCurrentDateTime() << "\n\n";

    std::sort(transactions.begin(), transactions.end(), [](const Transaction& a, const Transaction& b) {
        return a.getTimestamp() < b.getTimestamp();
    });

    for (const auto& transaction : transactions) {
        reportFile << "Transaction ID: " << transaction.getTransactionID() << "\n";
        reportFile << "Type: " << transaction.getType() << "\n";
        reportFile << "Amount: $" << std::fixed << std::setprecision(2) << transaction.getAmount() << "\n";
        reportFile << "Note: " << transaction.getNote() << "\n";
        time_t timestamp = transaction.getTimestamp();
        reportFile << "Timestamp: " << std::asctime(std::localtime(&timestamp)) << "\n";
        reportFile << "---------------------------------\n";
    }

    reportFile.close();

    std::cout << "Report successfully generated as \"" << filename << "\"\n";
    std::cout << "All transactions have been saved in chronological order.\n";

    std::cout << "\nPress Enter to return to main menu...\n";
    std::cin.ignore();
    std::cin.get();
}

void Bank::generateCustomerReport(const std::string& customerName) {
    Customer* customer = findCustomer(customerName);
    if (customer) {
        std::string filename = "transactions_" + customerName + "_" + Utility::getCurrentDateTime() + ".txt";

        std::ofstream reportFile(filename);
        if (!reportFile.is_open()) {
            std::cerr << "Failed to open file: " << filename << std::endl;
            return;
        }

        reportFile << "===== Customer Report: " << customerName << " =====\n";
        reportFile << "Generated on: " << Utility::getCurrentDateTime() << "\n\n";

        for (const auto& transaction : customer->getSavingsAccount()->getTransactions()) {
            reportFile << "Transaction ID: " << transaction.getTransactionID() << "\n";
            reportFile << "Type: " << transaction.getType() << "\n";
            reportFile << "Amount: $" << std::fixed << std::setprecision(2) << transaction.getAmount() << "\n";
            reportFile << "Note: " << transaction.getNote() << "\n";
            time_t timestamp = transaction.getTimestamp();
            reportFile << "Timestamp: " << std::asctime(std::localtime(&timestamp)) << "\n";
            reportFile << "---------------------------------\n";
        }

        for (const auto& transaction : customer->getCheckingAccount()->getTransactions()) {
            reportFile << "Transaction ID: " << transaction.getTransactionID() << "\n";
            reportFile << "Type: " << transaction.getType() << "\n";
            reportFile << "Amount: $" << std::fixed << std::setprecision(2) << transaction.getAmount() << "\n";
            reportFile << "Note: " << transaction.getNote() << "\n";
            time_t timestamp = transaction.getTimestamp();
            reportFile << "Timestamp: " << std::asctime(std::localtime(&timestamp)) << "\n";
            reportFile << "---------------------------------\n";
        }

        reportFile.close();

        std::cout << "Customer report successfully generated as \"" << filename << "\"\n";
        std::cout << "All transactions for " << customerName << " have been saved in chronological order.\n";

        std::cout << "\nPress Enter to return to main menu...\n";
        std::cin.ignore();
        std::cin.get();
    } else {
        std::cout << "Customer not found.\n";
    }
}

void Bank::generateAccountReport(const std::string& accountID) {
    if (accounts.find(accountID) != accounts.end()) {
        Account* account = accounts[accountID];
        std::string filename = "transactions_" + accountID + "_" + Utility::getCurrentDateTime() + ".txt";

        std::ofstream reportFile(filename);
        if (!reportFile.is_open()) {
            std::cerr << "Failed to open file: " << filename << std::endl;
            return;
        }

        reportFile << "===== Account Report: " << accountID << " =====\n";
        reportFile << "Generated on: " << Utility::getCurrentDateTime() << "\n\n";

        for (const auto& transaction : account->getTransactions()) {
            reportFile << "Transaction ID: " << transaction.getTransactionID() << "\n";
            reportFile << "Type: " << transaction.getType() << "\n";
            reportFile << "Amount: $" << std::fixed << std::setprecision(2) << transaction.getAmount() << "\n";
            reportFile << "Note: " << transaction.getNote() << "\n";
            time_t timestamp = transaction.getTimestamp();
            reportFile << "Timestamp: " << std::asctime(std::localtime(&timestamp)) << "\n";
            reportFile << "---------------------------------\n";
        }

        reportFile.close();

        std::cout << "Account report successfully generated as \"" << filename << "\"\n";
        std::cout << "All transactions for account " << accountID << " have been saved in chronological order.\n";

        std::cout << "\nPress Enter to return to main menu...\n";
        std::cin.ignore();
        std::cin.get();
    } else {
        std::cout << "Account not found.\n";
    }
}